<!doctype html>
<html lang="zh_cn" itemscope itemtype="http://schema.org/Person">
<head>
            <meta charset="utf-8">
        <!-- Site Meta Data -->
        <title>[原创]Vue2+iView2开发过程中踩过的坑</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="技术博客,涉及Java/PHP/Python/Javascript等,聊聊程序,聊聊生活,聊聊事实,聊聊育儿">
        <meta name="keywords" content="编码经验,技术分享,生活积累,实事评说">
        <meta name="author" content="布丁缘">

        <link rel="shortcut icon" href="">

        <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,600,700' rel='stylesheet' type='text/css'>
        <!-- Style Meta Data -->
        <link rel="stylesheet" href="https://www.ddkiss.com/theme/css/style.css" type="text/css"/>
        <link rel="stylesheet" href="https://www.ddkiss.com/theme/css/pygments.css" type="text/css"/>

        <!-- Feed Meta Data -->
            <link href="https://www.ddkiss.com/feeds/all.atom.xml" type="application/atom+xml" rel="alternate"
                  title="一个程序员的简单生活 ATOM Feed"/>


    <meta name="keywords" content="">
    <link rel="stylesheet" href="//dn-coding-net-public-file.qbox.me/Coding-Comments/v0.1.0/default.css">
</head>

<body>
<!-- Sidebar -->
<aside>
    <center><h1><a href="/" style="color:#fff"><img id="avatar" src="/images/avatar.jpg"></a></h1></center>
        <p>一个程序员的简单生活</p>
    <br>
    <nav class="nav">
        <ul class="list-bare">
                <li><a class="nav__link" href="https://www.ddkiss.com/category/chang-yong-ji-qiao.html">常用技巧</a></li>
                <li><a class="nav__link" href="https://www.ddkiss.com/category/kai-fa-huan-jing.html">开发环境</a></li>
                <li><a class="nav__link" href="https://www.ddkiss.com/category/sheng-huo-dian-di.html">生活点滴</a></li>

                <li><a class="nav__link" href="/pages/books.html">书单</a></li>
                <li><a class="nav__link" href="/pages/movies.html">影单</a></li>
                <li><a class="nav__link" href="/pages/downloads.html">下载</a></li>
                <li><a class="nav__link" href="/pages/about.html">关于我</a></li>


        </ul>
    </nav>


    <form>
      <input type="text"  id="bdcsMain"/>
    </form>

</aside>

<!-- Content -->
<article>
  <main>
    <nav>
      <a href="/">首页</a>
      <a href="/archives.html">归档</a>
      <a href="/categories.html">分类</a>
      <a href="/tags.html">标签</a>
      <a href="/pages/about.html">关于我</a>
    </nav>
  </main>
    <section id="content">
        <article>
            <h2 class="post_title post_detail"><a href="https://www.ddkiss.com/archives/30.html" rel="bookmark"
                                                  title="Permalink to [原创]Vue2+iView2开发过程中踩过的坑">[原创]Vue2+iView2开发过程中踩过的坑</a></h2>

            <div class="post_list">
                <span>作者：</span><a href="https://www.ddkiss.com/author/bu-ding-yuan.html">布丁缘</a>
                <span class="post_category">分类：<a href="https://www.ddkiss.com/category/kai-fa-huan-jing.html" rel="bookmark"
                                               title="Permalink to 开发环境">开发环境</a></span>
                <span class="post_date">  时间：2017-09-24 15:52:00</span>

            </div>
            <div class="entry-content blog-post">
                <h1>前言</h1>
<p>把官方文档基本看了一遍，但是还是有很多概念不是很熟。特别是vue基于webpack的脚手架工具。其中有些坑踩了才知道，顺便记录下。但类似npm等用法自行百度吧。架构也很简单，后台提供restful服务，以JSON格式进行数据传输，真正实现前后端分离。这里主要讲下 VUE2+iView2 实现前端功能过程中踩过的那些坑。IDE使用的是Atom+各种插件。</p>
<p>全局安装的插件</p>
<div class="highlight"><pre><span></span>#JavaScript 日期处理类库
cnpm install moment --save

#Javascript语法转换工具
cnpm install babel-polyfill --save
</pre></div>


<h1>问题</h1>
<h2>1. 编写.vue文件报错<code>Component template should contain exactly one root element</code></h2>
<p>代码很简单，如下</p>
<div class="highlight"><pre><span></span><span class="nt">&lt;template&gt;</span>

   <span class="nt">&lt;h1&gt;</span><span class="cp">{{</span><span class="nv">title</span><span class="cp">}}</span><span class="nt">&lt;/h1&gt;</span>

   <span class="nt">&lt;ol&gt;</span>

     <span class="nt">&lt;li</span> <span class="na">v-for=</span><span class="s">&quot;item in items&quot;</span><span class="nt">&gt;</span>

        <span class="cp">{{</span><span class="nv">item.text</span><span class="cp">}}</span>

     <span class="nt">&lt;/li&gt;</span>

   <span class="nt">&lt;/ol&gt;</span>

<span class="nt">&lt;/template&gt;</span>



<span class="nt">&lt;script&gt;</span>

export default {

  data () {

    return {

       title:&#39;this is a todo list&#39;,

       items:[

         {id:&#39;1&#39;,text:&#39;hello&#39;},

         {id:&#39;2&#39;,text:&#39;hello to some&#39;}

       ]

    }

  }

}

<span class="nt">&lt;/script&gt;</span>
</pre></div>


<p>提示是说没有 root 元素。 .vue 文件类似于一个组件，每个组件都必须遵循vue必须有一个根div，所以必须像下面的写法。</p>
<div class="highlight"><pre><span></span>   <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">&quot;todo&quot;</span><span class="nt">&gt;</span>

     <span class="nt">&lt;h1&gt;</span><span class="cp">{{</span><span class="nv">title</span><span class="cp">}}</span><span class="nt">&lt;/h1&gt;</span>

     <span class="nt">&lt;ol&gt;</span>

       <span class="nt">&lt;li</span> <span class="na">v-for=</span><span class="s">&quot;item in items&quot;</span><span class="nt">&gt;</span>

          <span class="cp">{{</span><span class="nv">item.text</span><span class="cp">}}</span>

       <span class="nt">&lt;/li&gt;</span>

     <span class="nt">&lt;/ol&gt;</span>

   <span class="nt">&lt;/div&gt;</span>
</pre></div>


<h2>2. iView中的Input组件无法响应@enter事件</h2>
<p>这是一个bug, 只能绕过。用原生的事件响应，解决办法如下：　@keyup.enter.native</p>
<h2>3. axios的拦截器配置问题</h2>
<p>如果设置了实例化如　var instance = axios.create();　则拦截器必须加在这个实例上，否则不会生效！！！</p>
<div class="highlight"><pre><span></span>var instance = axios.create();

instance.interceptors.request.use(function () {/*...*/});
</pre></div>


<h2>4. 关于数据不要求更新＆数据更新但视图未更新的问题</h2>
<p>如果数据不要求更新，则必须将原对象深拷贝，目前没有太好的办法只能　<code>newData = JSON.parse(JSON.stringify(oldData))</code></p>
<p>如果是数组，仅 <code>arr[index] = newData</code>, 是无法更新视图的。必须引入Vue，调用<code>Vue.set(items, index, newData)</code> 才可以更新。参考<a href="https://github.com/pfan123/front-end-navigator/issues/1">这里</a></p>
<p>附录：　</p>
<p>另一个方法　<code>newData = Object.assign({},oldData);</code></p>
<p>设置值　<code>items.splice(index, 1, newData);// 第一个是要修改的数组index, 第２个参数表示删除个数，第三个表示填入的值</code></p>
<h2>5. 关于路由的坑</h2>
<div class="highlight"><pre><span></span>路由跳转用　this.$router.push()

路由状态获取用　this.$route.params 等等。　就是　router  和 route 的区别
</pre></div>


<p>其中　params 是把参数加到url里，比如　query/111，而query是把参数加到url后缀里，比如　query?id=111</p>
<h2>6. 关于axios请求参数的问题</h2>
<p>如果使用 <code>params:{id:11}</code> 则参数最终会加到url上，　后端spring可以直接获取.</p>
<p>如果使用 <code>data:{id:11}</code>则参数放在请求的body里，仅限<code>post/put/patch</code>，　后端spring必须用 <code>@RequestBody</code>来定义要写入的对象</p>
<h2>7. iview关于table出现滚动条的问题</h2>
<p>很多地方无形触发了滚动条，太奇怪了。设置了很多都无效。最终定位到　<code>ivu-table-body</code>中写的 auto, 改为如下代码，但是不一定起作用……</p>
<div class="highlight"><pre><span></span><span class="na">.ivu-table-body</span> <span class="err">{</span>

    <span class="nl">overflow:</span> <span class="nf">hidden</span><span class="c">;</span>

<span class="err">}</span>
</pre></div>


<h2>8. iView中如何阻止Modal自动关闭</h2>
<p><code>iView</code>文档中只提供了<code>loading</code>属性，如果为<code>true</code>，则阻止回调关闭对话框。所以，出现错误时将此值设置为<code>true</code>可以保存对话框不关闭！这个跟我理解的<code>loading</code>正好是相反的！我认为<code>loading为true</code>时按钮会被禁用。。。其实不全是。。。 看了源码，确实是<code>loading为true</code>时才不会关闭对话框。但是，这个值又会影响<code>buttonLoading</code>。代码如下：</p>
<div class="highlight"><pre><span></span> ok () {

           if (this.loading) {

                      this.buttonLoading = true;

            } else {

                        this.visible = false;

                        this.$emit(&#39;input&#39;, false);

            }

            this.$emit(&#39;on-ok&#39;);

}



//buttonLoading　watch的代码

   loading (val) {

                if (!val) {

                    this.buttonLoading = false;

                }

            },
</pre></div>


<p>如果我想既不关闭对话框，又不出现buttonLoading，则必须用下面的代码hack。因为buttonLoading是watch算出来的。</p>
<div class="highlight"><pre><span></span>_this.opeBtnLoading = false;

setTimeout(() =&gt; { // 这段代码是必须的，方法完成前必须将其置为true

        _this.opeBtnLoading = true

}, 20);
</pre></div>


<p>暂时没想到更好的办法。更好的方式是通过方法调用吧。</p>
<h2>9. 如何删除数组＆视图得到更新</h2>
<p>参考<a href="http://blog.csdn.net/gaoxiaoba/article/details/52712136">这里</a>, 利用splice函数</p>
<h2>10. cookies操作</h2>
<p>参考<a href="https://www.npmjs.com/package/js-cookie">这里</a>，操作cookies文档</p>
<h2>11. 报错提示没有找到type</h2>
<p>错误信息如下：</p>
<div class="highlight"><pre><span></span><span class="p">[</span><span class="n">Vue</span> <span class="n">warn</span><span class="p">]</span><span class="o">:</span> <span class="n">Invalid</span> <span class="nl">prop</span><span class="p">:</span> <span class="n">custom</span> <span class="n">validator</span> <span class="n">check</span> <span class="n">failed</span> <span class="k">for</span> <span class="n">prop</span> <span class="s">&quot;type&quot;</span><span class="p">.</span>

<span class="n">found</span> <span class="k">in</span>

<span class="o">---&gt;</span> <span class="o">&lt;</span><span class="n">Button</span><span class="o">&gt;</span>

       <span class="o">&lt;</span><span class="n">FormItem</span><span class="o">&gt;</span>

         <span class="o">&lt;</span><span class="n">IForm</span><span class="o">&gt;</span>

           <span class="o">&lt;</span><span class="n">Row</span><span class="o">&gt;</span>

             <span class="o">&lt;</span><span class="n">ICol</span><span class="o">&gt;</span>

               <span class="o">&lt;</span><span class="n">Row</span><span class="o">&gt;</span>

                 <span class="o">&lt;</span><span class="n">Login</span><span class="o">&gt;</span> <span class="n">at</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">sang</span><span class="o">/</span><span class="n">workcode_wh</span><span class="o">/</span><span class="n">dise</span><span class="o">-</span><span class="n">vue</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">views</span><span class="o">/</span><span class="n">login</span><span class="p">.</span><span class="n">vue</span>

                   <span class="o">&lt;</span><span class="n">App</span><span class="o">&gt;</span> <span class="n">at</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">sang</span><span class="o">/</span><span class="n">workcode_wh</span><span class="o">/</span><span class="n">dise</span><span class="o">-</span><span class="n">vue</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">app</span><span class="p">.</span><span class="n">vue</span>

                     <span class="o">&lt;</span><span class="n">Root</span><span class="o">&gt;</span>
</pre></div>


<p>最后定位到是Button组件的type写错了</p>
<div class="highlight"><pre><span></span>  <span class="nt">&lt;Button</span> <span class="na">type=</span><span class="s">&quot;default&quot;</span><span class="nt">&gt;</span>重置<span class="nt">&lt;/Button&gt;</span>
</pre></div>


<p>所谓默认，<strong>其实就是不写type，而不是type="default"</strong></p>
<h2>12. webpack打包报错，UglifyJS无法压缩main.js</h2>
<p>参考<a href="/home/sang/workcode_oschina/iview-proj/.gitignore">这里</a>，　原因是无法解析es6语法，需要在项目下加入.babelrc文件，加入下面内容</p>
<div class="highlight"><pre><span></span>{

  &quot;presets&quot;: [

    [&quot;es2015&quot;, {

      &quot;modules&quot;: false

    }]

  ]

}
</pre></div>


<p>另外，还要加上包依赖</p>
<div class="highlight"><pre><span></span>    &quot;babel-preset-es2015&quot;: &quot;^6.9.0&quot;,
</pre></div>


<h2>13. 数字类型表单验证问题</h2>
<p>背景：用input组件，然后填入的是一个int类型，验证的时候是默认按string来做，无法校验成功</p>
<p>思路：通过查看iview input源码，默认填入的内容都是作为string来存储的，如果需要数字，则用input-number组件</p>
<p>解决办法：填入值之前，将其转成string类型。</p>
<h2>14. vuex在IE下报错，提示<code>vuex requires a Promise polyfill in this browser</code></h2>
<p>参考<a href="http://www.cnblogs.com/weiqinl/p/6794612.html">这里</a>。按如下步骤解决(更新：这个不适合iview cli的项目 )：</p>
<div class="highlight"><pre><span></span># 安装 babel polyfill，对es6的语法进行转换

 cnpm install --save babel-polyfill

＃配置webpack应用的js

entry: {

        main: &#39;./src/main&#39;,

        vendors: &#39;./src/vendors&#39;,

        app: [&#39;babel-polyfill&#39;, &#39;./src/main.js&#39;]  #在webpack.base.config.js中加入这个即可

    },
</pre></div>


<p>安装上面的方式做了之后，打包发现样式都没了！！！！估计是转换出问题了。尝试了很多遍都不行，最后发现了<a href="https://segmentfault.com/a/1190000009048684">这里</a>，因为iE下只是vuex需要Promise，所以只需要在store.js里顶部加入 import 'babel-polyfill' 即可,就这么简单！！！！差点就要放弃了。囧</p>
<h2>15. import绝对路径配置</h2>
<p>参考<a href="http://blog.csdn.net/neoveee/article/details/54968931">这里</a>，主要代码如下：</p>
<div class="highlight"><pre><span></span>resolve: {

        extensions: [&#39;.js&#39;, &#39;.vue&#39;],

        alias: {

            &#39;vue&#39;: &#39;vue/dist/vue.esm.js&#39;

        },

        modules: [

            path.resolve(&#39;./src&#39;),

            path.resolve(&#39;./node_modules&#39;)

        ]

    }
</pre></div>


<h2>16. 利用v-model实现子组件和父组件双向绑定</h2>
<p>参考<a href="http://blog.csdn.net/yangbingbinga/article/details/61914312">这里</a>，主要利用 $emit('input',value)来触发组件内input的双向绑定。</p>
<h2>17. 控件使用注意事项，可能是bug</h2>
<ol>
<li>
<p>DatePicker双向数据绑定是Date类型，表单回传前用js处理，或者回传到服务端后用java处理</p>
</li>
<li>
<p>DatePicker控件验证前要将绑定的值转成date类型，否则报value.getTime is not a function异常。处理方式是验证前通过xx.time = moment(xx.time).toDate()转成date型。参考<a href="https://segmentfault.com/q/1010000010015698">这里</a></p>
</li>
<li>
<p><code>&lt;Input type="textarea"&gt;</code>里面不能加入<code>:autosize="{minRows: 2,maxRows: 5}"</code>，否则会出现无法输入的问题。估计是bug？</p>
</li>
</ol>
<h2>18. 关于表单验证的一些问题</h2>
<p>默认的demo里的对象都是二级结构，正式使用时可能是三级结构或者以上。原来的写法就有问题，参考这里</p>
<div class="highlight"><pre><span></span><span class="nt">&lt;template&gt;</span>

<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">&quot;console-product&quot;</span><span class="nt">&gt;</span>

    <span class="nt">&lt;Form</span> <span class="na">ref=</span><span class="s">&quot;formValidate&quot;</span> <span class="na">:model=</span><span class="s">&quot;formValidate&quot;</span> <span class="na">:rules=</span><span class="s">&quot;ruleValidate&quot;</span> <span class="na">:label-width=</span><span class="s">&quot;80&quot;</span><span class="nt">&gt;</span>

      <span class="nt">&lt;Form-item</span> <span class="na">prop=</span><span class="s">&quot;date.subdate&quot;</span><span class="nt">&gt;</span>

          <span class="nt">&lt;Date-picker</span> <span class="na">type=</span><span class="s">&quot;date&quot;</span> <span class="na">placeholder=</span><span class="s">&quot;选择日期&quot;</span> <span class="na">v-model=</span><span class="s">&quot;formValidate.date.subdate&quot;</span><span class="nt">&gt;&lt;/Date-picker&gt;</span>

      <span class="nt">&lt;/Form-item&gt;</span>

    <span class="nt">&lt;/Form&gt;</span>

<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;/template&gt;</span>

<span class="nt">&lt;script&gt;</span>

export default {

    data() {

        return {

            formValidate: {

                date: {

                    subdate: &#39;&#39;

                }

            },

            ruleValidate: {

                &quot;date.subdate&quot;: [{

                    type: &#39;date&#39;

                    required: true,

                    message: &#39;姓名不能为空&#39;,

                    trigger: &#39;blur&#39;

                }]

            }

        }

    }

}

<span class="nt">&lt;/script&gt;</span>
</pre></div>


<p>注意点：</p>
<ol>
<li>
<p>时间类型一定要加上格式。比如 type:'date'，默认是string</p>
</li>
<li>
<p>多级结构时，rule里一定要用引号写，比如 "a.b.c.d":[{ required:true}]。否则是无法识别的！</p>
</li>
</ol>
<h2>19. setTimeout()方法注意事项</h2>
<div class="highlight"><pre><span></span>  setTimeout(() =&gt; { console.log(this.xxx) ;}, 5000);   //这里的this是vue全局变量

  setTimeout(function(){ console.log(this.xxx);},5000);　//这里的this是 windows对象
</pre></div>


<h2>20. 通过命名路由传递参数不显示在url中</h2>
<p>必须定义路由的名字，加入<code>name</code>属性</p>
<div class="highlight"><pre><span></span>name: &#39;visit_page&#39;,

path: &#39;visit/main&#39;,

component: (resolve) =&gt; require([&#39;./views/main/visit/main.vue&#39;], resolve),

       meta: {

            title: &#39;沟通记录列表&#39;

         }
</pre></div>


<p>然后，通过params塞入参数</p>
<div class="highlight"><pre><span></span>this.$router.push({

          name: &#39;visit_page&#39;,

          params: {id:&#39;1111&#39;}

 })
</pre></div>


<p>注意：　这里不能是<code>path</code>，坑。如果是 path，跳转后的页面是获取的 params 对象将是空的。参考<a href="http://blog.csdn.net/k491022087/article/details/70232965">这里</a></p>
<h2>21. webpack打包后的文件优化</h2>
<p>理解import的用法，去掉了一些无用的导入，大大减少了打包的体积。另外，把 iview.css 放到index.html里通过cdn引用也有一定效果。</p>
<h2>22. Keep-alive最佳实践</h2>
<p>做移动端开发，返回列表时会刷新页面，体验很差。通过keep-alive可以缓存列表页。具体用法参考<a href="https://segmentfault.com/a/1190000008123035">这里</a>。主要是通过配置路由，实现个别组件或路由的缓存。这样从详情页回到列表页时就会在原来的位置上保持不动了。</p>
<h1>声明</h1>
<p>转载请注明出处 http://www.ddkiss.com</p>
            </div>
            <div class="post_list">
              <div><span>Tags : </span>
              </div>
            </div>
        </article>
        <div id="container"></div>
        <script type="text/javascript" src="//dn-coding-net-public-file.qbox.me/Coding-Comments/v0.1.0/gitment.min.js"></script>
        <script>
            var gitment = new Gitment({
              owner: 'whusl',
              repo: 'BlogComments',
              oauth: {
                client_id: '621866266817529fba46681653017809',
                client_secret: '14188411740b12ae52159cee9b586bf85cd54125',
              },
            })
            document.getElementById('container').appendChild(gitment.render())
          </script>
    </section>
</article>

<!-- Footer -->
    <footer>
        <p> &copy;2017-2020&nbsp;<a href="http://www.miitbeian.gov.cn/" target="_blank">鄂ICP备17020200号</a>
          Blog powered by <a href="http://getpelican.com/">Pelican</a>
        </p>
    </footer>

    <!-- Analytics -->
    <script>
      var _hmt = _hmt || [];
      (function() {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?88c55edaf311dbacac56a16316b04c8b";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
      })();
    </script>

<script type="text/javascript">(function(){document.write(unescape('%3Cdiv id="bdcs"%3E%3C/div%3E'));var bdcs = document.createElement('script');bdcs.type = 'text/javascript';bdcs.async = true;bdcs.src = 'http://znsv.baidu.com/customer_search/api/js?sid=14490611060029767912' + '&plate_url=' + encodeURIComponent(window.location.href) + '&t=' + Math.ceil(new Date()/3600000);var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(bdcs, s);})();</script>

</body>
</html>